import Head from 'next/head';
import TableOptionsTable from '../../../components/prop-tables/TableOptionsTable';
import StateOptionsTable from '../../../components/prop-tables/StateOptionsTable';
import EnableExample from '../../../examples/enable-expanding-tree';
import ExpandedExample from '../../../examples/expanding-tree-expanded';
import ExpandedRootExample from '../../../examples/expanding-tree-root-expanded';
import ExpandingParsedTreeExample from '../../../examples/expanding-tree-flat-parse';
import LazySubRowsExample from '../../../examples/lazy-sub-rows';

<Head>
  <title>
    {'Expanding Sub-Rows/Tree Data Guide - Material React Table V3 Docs'}
  </title>
  <meta
    name="description"
    content="How to add expanding sub-rows or tree data features in Material React Table"
  />
</Head>

## Expanding Sub-Rows (Tree Data) Feature Guide

Material React Table has support for expanding sub-rows or tree data. This feature is useful for displaying hierarchical data. The sub-rows can be expanded and collapsed by clicking on the expand/collapse icon.

> NOTE: This feature is for expanding rows of the same data type. If you want to add expansion of more data for the same row, check out the [Detail Panel Feature Guide](/docs/guides/detail-panel).

### Relevant Table Options

<TableOptionsTable
  onlyOptions={
    new Set([
      'autoResetExpanded',
      'data',
      'enableExpandAll',
      'enableExpanding',
      'expandRowsFn',
      'filterFromLeafRows',
      'getExpandedRowModel',
      'getIsRowExpanded',
      'getRowCanExpand',
      'getRowId',
      'getSubRows',
      'groupedColumnMode',
      'manualExpanding',
      'maxLeafRowFilterDepth',
      'muiExpandAllButtonProps',
      'muiExpandButtonProps',
      'onExpandedChange',
      'paginateExpandedRows',
      'positionExpandColumn',
    ])

}
/>

### Relevant State Options

<StateOptionsTable onlyOptions={new Set(['expanded'])} />

### Enable Expanding Sub-Rows

To enable expanding sub-rows, you must first set the `enableExpanding` table option to `true`.

However, your data must also be formatted in a way to allow for expanding rows that are in some way related to each other. By default, Material React Table will look for a special `subRows` property on each row of your data and treat any array of rows that it finds as the sub-rows for that row. You can customize or override this behavior by passing a custom `getSubRows` table option.

```jsx
const data = [
  {
    id: 1,
    name: 'John Doe',
    subRows: [
      {
        id: 2,
        name: 'Jane Doe',
      },
      //more sub rows...
    ],
  },
  //more rows...
];

const table = useMaterialReactTable({
  columns,
  data,
  enableExpanding: true,
  getSubRows: (originalRow) => originalRow.subRows, //default, can customize
});

return <MaterialReactTable table={table} />;
```

### Expand All Rows Button

By default, Material React Table will show the expand all button in the expand column header. You can disable this by setting the `enableExpandAll` table option to `false`.

```jsx
const table = useMaterialReactTable({
  columns,
  data,
  enableExpanding: true,
  enableExpandAll: false, //hide expand all button in header
});
```

<EnableExample />

### Generate Sub Rows with getSubRows

If your data is not yet in a tree structure, but the data has relationships that can be parsed into a tree, you can use the `getSubRows` table option to let TanStack Table find the sub rows for each row.

There are a couple key things you have to do to make this work:

1. Only pass in root (top level) rows in your data prop.
2. Set the `getSubRows` table option to a function that scans all the rest of your data and returns the sub rows for a given row.

This can sometimes be useful in combination with [lazy loading sub rows](#lazy-load-sub-rows).

> NOTE: Be conscious of the performance implications of the `getSubRows` function. It will be called for every row in your table, so it should be performant.

<ExpandingParsedTreeExample />

### Expanded Rows Pagination Behavior

By default, Material React Table will treat expanded sub-rows the same as any other row when it comes to pagination. This means that some expanded rows may be on the next page. You can change this behavior by setting the `paginateExpandedRows` table option to `false`.

```jsx
const table = useMaterialReactTable({
  columns,
  data,
  enableExpanding: true,
  paginateExpandedRows: false, //expanded rows will be on the same page as their parent row
});
```

### Expanded Leaf Row Filtering Behavior

If you are using the [filtering features](/docs/guides/column-filtering) alongside sub-row features, then there are a few behaviors and customizations you should be aware of.

#### Filter From Leaf Rows

By default, filtering is done from parent rows down (so if a parent row is filtered out, all of its children will be filtered out as well). Setting the `filterFromLeafRows` table option to `true` will cause filtering to be done from leaf rows up (which means parent rows will be kept so long as one of their child, or grand-child, etc. rows pass the filtering).

```jsx
const table = useMaterialReactTable({
  columns,
  data,
  enableExpanding: true,
  filterFromLeafRows: true, //search for child rows and preserve parent rows
});
```

#### Max Leaf Row Filter Depth

By default, filtering is done for all rows (max depth of 100), no matter if they are root level parent rows or the child leaf rows of a parent row. Setting the `maxLeafRowFilterDepth` table option to `0` will cause filtering to only be applied to the root level parent rows, with all sub-rows remaining unfiltered. Similarly, setting this option to 1 will cause filtering to only be applied to child leaf rows 1 level deep, and so on.

This is useful for situations where you want a row's entire child hierarchy to be visible, regardless of the applied filter.

```jsx
const table = useMaterialReactTable({
  columns,
  data,
  enableExpanding: true,
  maxLeafRowFilterDepth: 0, //When filtering root rows, keep all child rows of the passing parent rows
});
```

### Expand All Rows By Default

You can manage the initial state of the expanded rows with the `expanded` state option in either the `initialState` or `state` props.

For example, you may want all rows to be expanded by default. To do this, you can simply set the `expanded` state option to `true`.

```jsx
const table = useMaterialReactTable({
  columns,
  data,
  enableExpanding: true,
  initialState: { expanded: true }, //all rows expanded by default
});
```

<ExpandedExample />

### Expand Root Rows Only By Default

Here is a slightly more complex initial expanded state example where all the root rows are expanded by default, but none of the sub rows themselves are expanded by default. We just need to find all of the root row ids and set their key in the `expanded` `initialState` option to `true`.

<ExpandedRootExample />

### Customize Expand Column

You can customize the expand column by using the `displayColumnDefOptions` table option.

```jsx
const table = useMaterialReactTable({
  columns,
  data,
  enableGrouping: true,
  displayColumnDefOptions: {
    'mrt-row-expand': {
      enableResizing: true, //allow resizing
      size: 120, //make the expand column wider
    },
  },
});
```

### Lazy Load Sub Rows

If you have a ton of nested data that you want to display, but you don't want to fetch it all up front, you can set up Material React Table to only fetch the sub-rows data when the user expands the row.

There are quite a few ways in which you could implement fetching sub-rows lazily. This example is just one way to do it.

The main concept to understand from this example is that you can manage the `expanded` state option in your own scope, and fetch the data for your table based on that state.

How your data is structured from the server is up to you. It is usually easiest to have the server do the hard work and return the data in a nested tree structure, but you can also return the data in a flat structure and use the `getSubRows` table option to parse the data into a tree structure.

<LazySubRowsExample />
